home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 October: Mac OS SDK / Dev.CD Oct 00 SDK1.toast / Development Kits / Mac OS / MLTE SDK / TEtoMLTESample / TESources / TEDocument.cp next >
Encoding:
Text File  |  1999-12-05  |  31.8 KB  |  1,069 lines  |  [TEXT/MPS ]

  1. /*------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    MultiFinder-Aware Simple TextEdit Sample Application
  6. #
  7. #    CPlusTESample
  8. #
  9. #    TEDocument.cp    -    C++ source
  10. #
  11. #    Copyright © 1989 Apple Computer, Inc.
  12. #    All rights reserved.
  13. #
  14. #    Versions:    
  15. #            1.10                     07/89
  16. #            1.00                     04/89
  17. #
  18. #    Components:
  19. #            CPlusTESample.make        July 9, 1989
  20. #            TApplicationCommon.h    July 9, 1989
  21. #            TApplication.h            July 9, 1989
  22. #            TDocument.h                July 9, 1989
  23. #            TECommon.h                July 9, 1989
  24. #            TESample.h                July 9, 1989
  25. #            TEDocument.h            July 9, 1989
  26. #            TApplication.cp            July 9, 1989
  27. #            TDocument.cp            July 9, 1989
  28. #            TESample.cp                July 9, 1989
  29. #            TEDocument.cp            July 9, 1989
  30. #            TESampleGlue.a            July 9, 1989
  31. #            TApplication.r            July 9, 1989
  32. #            TESample.r                July 9, 1989
  33. #
  34. #    CPlusTESample is an example application that demonstrates
  35. #    how to initialize the commonly used toolbox managers,
  36. #    operate successfully under MultiFinder, handle desk
  37. #    accessories and create, grow, and zoom windows. The
  38. #    fundamental TextEdit toolbox calls and TextEdit autoscroll
  39. #    are demonstrated. It also shows how to create and maintain
  40. #    scrollbar controls. 
  41. #
  42. #    This version of TESample has been substantially reworked in
  43. #    C++ to show how a "typical" object oriented program could
  44. #    be written. To this end, what was once a single source code
  45. #    file has been restructured into a set of classes which
  46. #    demonstrate the advantages of object-oriented programming.
  47. #
  48. ------------------------------------------------------------------------------*/
  49.  
  50.  
  51. /*
  52. Segmentation strategy:
  53.  
  54.     This program has only one segment, since the issues
  55.     surrounding segmentation within a class's methods have
  56.     not been investigated yet. We DO unload the data
  57.     initialization segment at startup time, which frees up
  58.     some memory 
  59.  
  60. SetPort strategy:
  61.  
  62.     Toolbox routines do not change the current port. In
  63.     spite of this, in this program we use a strategy of
  64.     calling SetPort whenever we want to draw or make calls
  65.     which depend on the current port. This makes us less
  66.     vulnerable to bugs in other software which might alter
  67.     the current port (such as the bug (feature?) in many
  68.     desk accessories which change the port on OpenDeskAcc).
  69.     Hopefully, this also makes the routines from this
  70.     program more self-contained, since they don't depend on
  71.     the current port setting. 
  72.  
  73. Clipboard strategy:
  74.  
  75.     This program does not maintain a private scrap.
  76.     Whenever a cut, copy, or paste occurs, we import/export
  77.     from the public scrap to TextEdit's scrap right away,
  78.     using the TEToScrap and TEFromScrap routines. If we did
  79.     use a private scrap, the import/export would be in the
  80.     activate/deactivate event and suspend/resume event
  81.     routines. 
  82. */
  83.  
  84. // Mac Includes
  85. #include <Types.h>
  86. #include <Quickdraw.h>
  87. #include <Fonts.h>
  88. #include <Controls.h>
  89. #include <Windows.h>
  90. #include <TextEdit.h>
  91. #include <Dialogs.h>
  92. #include <Menus.h>
  93. #include <Devices.h>
  94. #include <Events.h> 
  95. #include <Scrap.h>
  96. #include <ToolUtils.h>
  97. #include <Memory.h>
  98. #include <SegLoad.h>
  99. #include <Files.h>
  100. #include <OSUtils.h>
  101. #include <Traps.h>
  102. #include <LowMem.h>
  103. #include <Controls.h>
  104. #include <TextUtils.h>
  105.  
  106. #include "TEDocument.h"
  107. // we need resource definitions
  108. #include "TECommon.h"
  109.  
  110. // #include "TESample.h"
  111.  
  112. extern "C"
  113.     // prototypes for functions that don't belong to any one class
  114.         pascal     TEClickLoopUPP GetOldClickLoop(void);
  115.         pascal     void PascalClickLoop(void);
  116.         void     CommonAction(ControlHandle control,short* amount);
  117.         pascal     void VActionProc(ControlHandle control,short part);
  118.         pascal     void HActionProc(ControlHandle control,short part);
  119.     
  120.     
  121.     // ASMCLICKLOOP is written in Assembler for the 68K, since it needs to tweak
  122.     // registers.  For the PowerPC, we can write this routine in C because 
  123.     // MixedMode gives us a C interface for this.
  124.     
  125.        pascal Boolean ASMCLICKLOOP (TEPtr pTE);
  126.  
  127. };
  128.  
  129. // kTextMargin is the number of pixels we leave blank at the edge of the window.
  130.     const short kTextMargin = 2;
  131.  
  132. // kMaxDocWidth is an arbitrary number used to specify the width of the TERec's
  133. // destination rectangle so that word wrap and horizontal scrolling can be
  134. // demonstrated.
  135.     const short    kMaxDocWidth = 576;
  136.     
  137. // kMinDocDim is used to limit the minimum dimension of a window when GrowWindow
  138. // is called.
  139.     const short    kMinDocDim = 64;
  140.     
  141. // kMaxTELength is an arbitrary number used to limit the length of text in the TERec
  142. // so that various errors won't occur from too many characters being in the text.
  143.     const short    kMaxTELength = 32000;
  144.  
  145. // kControlInvisible is used the same way to 'turn on' the control.
  146.     const short kControlVisible = 0xFF;
  147.  
  148. // ScrollBarAdjust, GrowBoxAdjust, and ScrollBar width are used in calculating
  149. // values for control positioning and sizing.
  150.     const short kScrollbarAdjust = 15;
  151.     const short kGrowboxAdjust = 15;
  152.     const short kScrollbarWidth = 16;
  153.  
  154. // kTESlop provides some extra security when pre-flighting edit commands.
  155.     const short kTESlop = 1024;
  156.  
  157. // kScrollTweek compensates for off-by-one requirements of the scrollbars
  158. // to have borders coincide with the growbox.
  159.     const short kScrollTweek = 2;
  160.     
  161. // kCrChar is used to match with a carriage return when calculating the
  162. // number of lines in the TextEdit record. kDelChar is used to check for
  163. // delete in keyDowns.
  164.     const short kCrChar = 13;
  165.     const short kDelChar = 8;
  166.     
  167. // Use universal procedure pointers that are compatible with both 68K and PowerPC.
  168.  
  169.     ControlActionUPP VActionDesc;
  170.     ControlActionUPP HActionDesc;
  171.     TEClickLoopUPP   TEClickLoopDesc;
  172.     Boolean             inited = false;
  173.     
  174.  
  175. /***********************************************************************/
  176. //
  177. // TEDocument class declarations
  178. //
  179. /***********************************************************************/
  180.  
  181. //-----------------------------------------------------------------------
  182. // TEDocument::TEDocument -    notice that we pass the resID parameter up to our
  183. //                            base class, which actually creates the window for us.
  184. //                            If you modify this so that it doesn't create a
  185. //                            window, be sure to take that into account in the
  186. //                            application class so that it doesn't believe an
  187. //                            error occurred (as version 1.20 of TApplication
  188. //                            would).
  189. //
  190.     TEDocument::TEDocument( short resID )    : TDocument( resID )
  191.     {
  192.         Boolean good;
  193.         Rect destRect, viewRect;
  194.         
  195.         // initialize routine desciptors once
  196.         if (!inited)
  197.         {
  198.             VActionDesc = NewControlActionProc (VActionProc);
  199.             HActionDesc = NewControlActionProc (HActionProc);
  200.             TEClickLoopDesc = NewTEClickLoopProc (ASMCLICKLOOP);
  201.             inited = true;
  202.         }
  203.  
  204.         good = false;
  205.         SetPort( fDocWindow );
  206.         GetTERect( &viewRect );
  207.         destRect = viewRect;
  208.         destRect.right = destRect.left + kMaxDocWidth;
  209.         fDocTE = TENew( &destRect, &viewRect );
  210.         
  211.         good = fDocTE != nil;    // if TENew succeeded, we have a good document 
  212.     
  213.         // if it's a valid document, initialize the text edit fields
  214.         // and try to create a vertical scroll control
  215.             if ( good )
  216.             {
  217.                 // set up TE record
  218.                     AdjustViewRect();
  219.                     TEAutoView( true, fDocTE );
  220.                     fDocClick = (*fDocTE)->clickLoop;
  221.                     (*fDocTE)->clickLoop =  TEClickLoopDesc;
  222.  
  223.                 // get vertical scrollbar
  224.                     fDocVScroll = GetNewControl( rVScroll, fDocWindow );
  225.                     good = ( fDocVScroll != nil );
  226.             }
  227.         
  228.         // if the vertical scroll control has been properly created
  229.         // try to create a horizontal one. otherwise, don't bother
  230.             if ( good )
  231.             {
  232.                 fDocHScroll = GetNewControl( rHScroll, fDocWindow );
  233.                 good = ( fDocHScroll != nil );
  234.             }
  235.         
  236.         // If both horizontal and vertical controls have been created
  237.         // then we can go ahead and use this new document, so show it.
  238.         // Otherwise, let the user know that we have a problem.
  239.             if ( good )                
  240.             {                                                        // adjust & draw the controls, draw the window
  241.                 SetWindowDoc( fDocWindow, this );
  242.                 AdjustScrollValues( true );
  243.                 ShowWindow( fDocWindow );
  244.             }
  245.             else
  246.                 AlertUser( kTEDocErrStrings, eNoWindow );            // tell user we failed
  247.             
  248.     } /* TEDocument (constructor) */
  249.  
  250.  
  251. //-----------------------------------------------------------------------
  252. // TEDocument::~TEDocument -    At this point, if there was a document associated 
  253. //                                with a window, you could do any document saving
  254. //                                processing if it is 'dirty'. DoCloseWindow would
  255. //                                return true if the window actually closed, i.e.,
  256. //                                the user didn’t cancel from a save dialog. This
  257. //                                result is handy whenthe user quits an application,
  258. //                                but then cancels the save of a documentassociated
  259. //                                with a window.
  260. //
  261.     TEDocument::~TEDocument(void)
  262.     {
  263.         HideWindow( fDocWindow );
  264.         
  265.         // get rid of any extra storage that was used for the window
  266.             if ( fDocTE != nil )
  267.                 TEDispose(fDocTE);                                    // dispose the TEHandle if we got far enough to make one 
  268.         
  269.             if ( fDocVScroll != nil )                                // don't forget about the control storage
  270.                 DisposeControl(fDocVScroll);
  271.     
  272.             if ( fDocHScroll != nil )
  273.                 DisposeControl(fDocHScroll);
  274.     
  275.         // base class destructor will dispose of window
  276.     } /* TEDocument (destructor) */
  277.  
  278.  
  279. //-----------------------------------------------------------------------
  280. // TEDocument::DoZoom -     
  281. //
  282.     void TEDocument::DoZoom( short partCode )
  283.     {
  284.         Rect tRect;
  285.     
  286.         tRect = fDocWindow->portRect;
  287.         EraseRect( &tRect );
  288.         ZoomWindow( fDocWindow, partCode, fDocWindow == FrontWindow());
  289.         AdjustScrollbars(true);            // adjust, redraw anyway 
  290.         AdjustTE();
  291.         InvalRect( &tRect );            // invalidate the whole content 
  292.         
  293.         // the scrollbars were taken care of by AdjustScrollbars, so validate ’em 
  294.             tRect = (*fDocVScroll)->contrlRect;
  295.             ValidRect( &tRect );
  296.             tRect = (*fDocHScroll)->contrlRect;
  297.             ValidRect( &tRect );
  298.         
  299.     } /* TEDocument::DoZoom */
  300.  
  301.  
  302. //-----------------------------------------------------------------------
  303. // TEDocument::DoGrow -    Called when a mouseDown occurs in the grow box of
  304. //                        an active window.
  305. //
  306.     void TEDocument::DoGrow( EventRecord* theEvent )
  307.     {
  308.         long growResult;
  309.         Rect tRect, tRect2;
  310.         
  311.         tRect = qd.screenBits.bounds;
  312.         tRect.left = kMinDocDim;
  313.         tRect.top = kMinDocDim;
  314.         growResult = GrowWindow(fDocWindow, theEvent->where, &tRect);
  315.         
  316.         // see if it really changed size 
  317.             if ( growResult != 0 )
  318.             {
  319.                 tRect = (*fDocTE)->viewRect;
  320.                 SizeWindow(fDocWindow, LoWrd(growResult), HiWrd(growResult), true);
  321.                 AdjustScrollbars(true);
  322.                 AdjustTE();
  323.                 
  324.                 // calculate & validate the region that hasn’t changed so it won’t get redrawn
  325.                 // Note: we copy rectangles so that we don't take address of object fields.
  326.                     tRect2 = (*fDocTE)->viewRect;
  327.                     (void) SectRect(&tRect, &tRect2, &tRect);
  328.                     tRect2 = fDocWindow->portRect; InvalRect(&tRect2);
  329.                     ValidRect(&tRect);
  330.                     tRect2 = (*fDocVScroll)->contrlRect; ValidRect(&tRect2);
  331.                     tRect2 = (*fDocHScroll)->contrlRect; ValidRect(&tRect2);
  332.             }
  333.     } /* TEDocument::DoGrow */
  334.  
  335.  
  336. //-----------------------------------------------------------------------
  337. // TEDocument::DoContent -    
  338. //
  339.     void TEDocument::DoContent( EventRecord* theEvent )
  340.     {
  341.         Point mouse;
  342.         ControlHandle control;
  343.         short part, value;
  344.         Boolean shiftDown;
  345.         Rect teRect;
  346.     
  347.         SetPort( fDocWindow );
  348.         mouse = theEvent->where;                            // get the click position 
  349.         GlobalToLocal( &mouse );
  350.         GetTERect( &teRect );
  351.         if ( PtInRect( mouse, &teRect ))
  352.         {
  353.             /* see if we need to extend the selection */
  354.                 shiftDown = ( theEvent->modifiers & shiftKey ) != 0;    /* extend if Shift is down */
  355.                 TEClick( mouse, shiftDown, fDocTE );
  356.         }
  357.         else
  358.         {
  359.             part = FindControl( mouse, fDocWindow, &control );
  360.             switch ( part )
  361.             {
  362.                 case 0:            break;        // do nothing if not in a control
  363.                     
  364.                 case kControlIndicatorPart:
  365.                                 value = GetControlValue( control );
  366.                                 part = TrackControl( control, mouse, nil );
  367.                                 if ( part != 0 )
  368.                                 {
  369.                                     value -= GetControlValue(control);
  370.                                     
  371.                                     // value now has CHANGE in value; if value changed, scroll 
  372.                                         if ( value != 0 )
  373.                                             if ( control == fDocVScroll )
  374.                                                 TEScroll(0, value * (*fDocTE)->lineHeight, fDocTE);
  375.                                             else
  376.                                                 TEScroll(value, 0, fDocTE);
  377.                                 }
  378.                                 break;
  379.                     
  380.                 default:        if ( control == fDocVScroll )                                        // they clicked in an arrow, so track & scroll 
  381.                                     value = TrackControl( control, mouse,  VActionDesc );
  382.                                 else
  383.                                     value = TrackControl( control, mouse,  HActionDesc );
  384.                                 break;                        
  385.             }
  386.         }
  387.     } /* TEDocument::DoContent */
  388.  
  389.  
  390. //-----------------------------------------------------------------------
  391. // TEDocument::DoKeyDown -    
  392. //
  393.     void TEDocument::DoKeyDown( EventRecord* theEvent )
  394.     {
  395.         char key;
  396.     
  397.         if ( theEvent->modifiers & cmdKey )                            // don't process command characters
  398.             return;
  399.         key = (char) ( theEvent->message & charCodeMask );
  400.         
  401.         // we have a char. for our window; see if we are still below TextEdit’s
  402.         // limit for the number of characters
  403.             if (( key == kDelChar ) || ((*fDocTE)->teLength - ((*fDocTE)->selEnd - (*fDocTE)->selStart ) + 1 < kMaxTELength ))
  404.             {
  405.                 TEKey(key, fDocTE);
  406.                 AdjustScrollbars(false);
  407.                 AdjustTE();
  408.             }
  409.             else
  410.                 AlertUser( kTEDocErrStrings, eExceedChar );
  411.             
  412.     } /* TEDocument::DoKeyDown */
  413.  
  414.  
  415. //-----------------------------------------------------------------------
  416. // TEDocument::DoActivate -    
  417. //
  418.     void TEDocument::DoActivate( Boolean becomingActive )
  419.     {
  420.         if ( becomingActive )
  421.         {
  422.             RgnHandle    tempRgn;
  423.             RgnHandle    clipRgn;
  424.             Rect        growRect;
  425.             Rect        tRect;
  426.     
  427.             // since we don’t want TEActivate to draw a selection in an area where
  428.             // we’re going to erase and redraw, we’ll clip out the update region
  429.             // before calling it.
  430.                 tempRgn = NewRgn();
  431.                 clipRgn = NewRgn();
  432.                 
  433.             // save old update region
  434.                 CopyRgn(((WindowPeek) fDocWindow)->updateRgn, tempRgn );
  435.                 
  436.             // put it in local coords
  437.                 OffsetRgn( tempRgn, fDocWindow->portBits.bounds.left, fDocWindow->portBits.bounds.top );
  438.                 GetClip( clipRgn );
  439.                 
  440.             // subtract updateRgn from clipRgn
  441.                 DiffRgn( clipRgn, tempRgn, tempRgn );
  442.             
  443.             // make it the new clipRgn
  444.                 SetClip( tempRgn );
  445.                 TEActivate( fDocTE );
  446.                 
  447.             // restore the full-blown clipRgn
  448.                 SetClip( clipRgn );
  449.                 
  450.             // get rid of temp regions
  451.                 DisposeRgn( tempRgn );
  452.                 DisposeRgn( clipRgn );
  453.     
  454.             // the controls must be redrawn on activation
  455.                 (*fDocVScroll)->contrlVis = kControlVisible;
  456.                 (*fDocHScroll)->contrlVis = kControlVisible;
  457.                 
  458.             // copy rectangles to avoid unsafe object field references!
  459.                 tRect = (*fDocVScroll)->contrlRect; InvalRect( &tRect );
  460.                 tRect = (*fDocHScroll)->contrlRect; InvalRect( &tRect );
  461.  
  462.             // the growbox needs to be redrawn on activation:
  463.                 growRect = fDocWindow->portRect;
  464.  
  465.             // adjust for the scrollbars
  466.                 growRect.top = growRect.bottom - kScrollbarAdjust;
  467.                 growRect.left = growRect.right - kScrollbarAdjust;
  468.                 InvalRect( &growRect );
  469.         }
  470.         else
  471.         {        
  472.             TEDeactivate(fDocTE);
  473.         
  474.             // the controls must be hidden on deactivation
  475.                 HideControl(fDocVScroll);
  476.                 HideControl(fDocHScroll);
  477.         
  478.             // we draw grow icon immediately, since we deactivate controls
  479.             // immediately, and the update delay looks funny
  480.                 DrawGrowIcon(fDocWindow);
  481.         }
  482.           
  483.     } /* TEDocument::DoActivate */
  484.  
  485.  
  486. //-----------------------------------------------------------------------
  487. // TEDocument::DoUpdate -    
  488. //
  489.     void TEDocument::DoUpdate(void)
  490.     {
  491.         BeginUpdate( fDocWindow );                // this sets up the visRgn 
  492.         if ( ! EmptyRgn( fDocWindow->visRgn ))    // draw if updating needs to be done 
  493.         {
  494.             DrawWindow();
  495.         }
  496.         EndUpdate( fDocWindow );
  497.         
  498.     } /* TEDocument::DoUpdate */
  499.  
  500.  
  501. //-----------------------------------------------------------------------
  502. // TEDocument::CalcIdle -    calculate how much idle time we need
  503. //
  504.     unsigned long TEDocument::CalcIdle(void)
  505.     {
  506.         if ( HaveSelection())
  507.             return GetCaretTime();                // interval needed to blink insertion caret
  508.         else
  509.             return kMaxSleepTime;                // if we don't have a selection, we don't need to idle
  510.         
  511.     } /* TEDocument::CalcIdle */
  512.  
  513.  
  514. //-----------------------------------------------------------------------
  515. // TEDocument::DoIdle -    This is called whenever we get a null event et al.
  516. //                         It takes care of necessary periodic actions. For
  517. //                         this program, it calls TEIdle.
  518. //
  519.     void TEDocument::DoIdle( void )
  520.     {
  521.         TEIdle( fDocTE );
  522.         
  523.     } /* TEDocument::DoIdle */
  524.  
  525.  
  526. //-----------------------------------------------------------------------
  527. // TEDocument::DrawWindow -    Draw the contents of an application window.
  528. //
  529.     void TEDocument::DrawWindow(void)
  530.     {
  531.         Rect tRect;
  532.     
  533.         SetPort( fDocWindow );
  534.         tRect = fDocWindow->portRect;
  535.         EraseRect( &tRect );
  536.         TEUpdate( &tRect, fDocTE );
  537.         DrawControls( fDocWindow );
  538.         DrawGrowIcon( fDocWindow );
  539.         
  540.     } /* TEDocument::DrawWindow */
  541.  
  542.  
  543. //-----------------------------------------------------------------------
  544. // TEDocument::GetTERect -    Return a rectangle that is inset from the portRect
  545. //                             by the size of the scrollbars and a little extra margin.
  546. //
  547.     void TEDocument::GetTERect( Rect* teRect )
  548.     {
  549.         *teRect = fDocWindow->portRect;
  550.         InsetRect( teRect, kTextMargin, kTextMargin );            // adjust for margin 
  551.         teRect->bottom = teRect->bottom - kScrollbarAdjust;        // and for the scrollbars 
  552.         teRect->right = teRect->right - kScrollbarAdjust;
  553.         
  554.     } /* TEDocument::GetTERect */
  555.  
  556.  
  557. //-----------------------------------------------------------------------
  558. // TEDocument::GetVisTERgn -    setup a region which contains the visible text
  559. //
  560.     void TEDocument::GetVisTERgn(RgnHandle rgn)
  561.     {
  562.         Rect teRect;
  563.     
  564.         teRect = (*fDocTE)->viewRect;                            // get a local copy of viewRect
  565.         SetPort(fDocWindow);                                    // make sure we have right port
  566.         LocalToGlobal(&TopLeft(teRect));
  567.         LocalToGlobal(&BotRight(teRect));
  568.         RectRgn(rgn, &teRect);
  569.  
  570.         // we temporarily change the port’s origin to “globalfy” the visRgn
  571.             SetOrigin( -( fDocWindow->portBits.bounds.left ), -( fDocWindow->portBits.bounds.top ));
  572.             SectRgn(rgn, fDocWindow->visRgn, rgn );
  573.             SetOrigin( 0, 0 );
  574.             
  575.     } /* TEDocument::GetVisTERgn */
  576.  
  577.  
  578. //-----------------------------------------------------------------------
  579. // TEDocument::HaveSelection -    Return boolean value indicating that there is
  580. //                                 or is not a selection in the document
  581. //
  582.     Boolean TEDocument::HaveSelection()
  583.     {
  584.         if ((*fDocTE)->selStart < (*fDocTE)->selEnd )
  585.             return true;
  586.         else
  587.             return false;
  588.             
  589.     } /* TEDocument::HaveSelection */
  590.  
  591.  
  592. //-----------------------------------------------------------------------
  593. // TEDocument::AdjustViewRect -    Update the TERec's view rect so that it is the
  594. //                                 greatest multiple ofthe line Height that still
  595. //                                fits in the old viewRect.
  596. //
  597.     void TEDocument::AdjustViewRect(void)
  598.     {
  599.         TEPtr te;
  600.         
  601.         te = *fDocTE;
  602.         te->viewRect.bottom = (((te->viewRect.bottom - te->viewRect.top) / te->lineHeight)
  603.                                 * te->lineHeight) + te->viewRect.top;
  604.                                 
  605.     } /* TEDocument::AdjustViewRect */
  606.  
  607.  
  608. //-----------------------------------------------------------------------
  609. // TEDocument::AdjustTE -    Scroll the TERec around to match up to the potentially
  610. //                             updated scrollbar values. This is really useful when
  611. //                             the window has been resized such that the scrollbars
  612. //                            became inactive but the TERec was already scrolled.
  613. //
  614.     void TEDocument::AdjustTE( void )
  615.     {
  616.         TEPtr te;
  617.         
  618.         te = *fDocTE;
  619.         TEScroll(( te->viewRect.left - te->destRect.left ) - GetControlValue( fDocHScroll ),
  620.                  ( te->viewRect.top - te->destRect.top ) -  (GetControlValue( fDocVScroll ) * te->lineHeight ),
  621.                  fDocTE );
  622.                  
  623.     } /* TEDocument::AdjustTE */
  624.  
  625.  
  626. //-----------------------------------------------------------------------
  627. // TEDocument::AdjustScrollSizes -    Re-calculate the position and size of the
  628. //                                     viewRect and the scrollbars. kScrollTweek
  629. //                                    compensates for off-by-one requirements of
  630. //                                     the scrollbars to have borders coincide
  631. //                                    with the growbox.
  632. //
  633.     void TEDocument::AdjustScrollSizes(void)
  634.     {
  635.         Rect teRect;
  636.         
  637.         GetTERect( &teRect );
  638.         (*fDocTE)->viewRect = teRect;
  639.         AdjustViewRect();
  640.         MoveControl( fDocVScroll, fDocWindow->portRect.right - kScrollbarAdjust, -1 );
  641.         SizeControl( fDocVScroll, kScrollbarWidth,
  642.                      fDocWindow->portRect.bottom - fDocWindow->portRect.top - kGrowboxAdjust + kScrollTweek );
  643.         MoveControl(fDocHScroll, -1, fDocWindow->portRect.bottom - kScrollbarAdjust);
  644.         SizeControl(fDocHScroll,
  645.                     fDocWindow->portRect.right - fDocWindow->portRect.left - kGrowboxAdjust + kScrollTweek,
  646.                     kScrollbarWidth);
  647.                     
  648.     } /* TEDocument::AdjustScrollSizes */
  649.  
  650.  
  651. //-----------------------------------------------------------------------
  652. // TEDocument::AdjustScrollbars -    Turn off the controls by jamming a zero into
  653. //                                    their contrlVis fields (HideControl erases them
  654. //                                    and we don't want that). If the controls are to
  655. //                                    be resized as well, call the procedure to do that,
  656. //                                    then call the procedure to adjust the maximum and
  657. //                                    current values. Finally re-enable the controls by
  658. //                                    jamming a $FF in their contrlVis fields (ShowControl
  659. //                                    re-draws the control, which may not be necessary).
  660. //
  661.     void TEDocument::AdjustScrollbars( Boolean needsResize )
  662.     {
  663.         // First, turn visibility of scrollbars off so we won’t get unwanted redrawing 
  664.             (*fDocVScroll)->contrlVis = 0;
  665.             (*fDocHScroll)->contrlVis = 0;
  666.             if ( needsResize )
  667.                 AdjustScrollSizes();
  668.             AdjustScrollValues( needsResize );
  669.             
  670.         // Now, restore visibility in case we never had to draw during adjustment 
  671.             (*fDocVScroll)->contrlVis = 0xff;
  672.             (*fDocHScroll)->contrlVis = 0xff;
  673.         
  674.     } /* TEDocument::AdjustScrollbars */
  675.  
  676. // Calculate the new control maximum value and current value, whether it is the horizontal or
  677.  
  678. //-----------------------------------------------------------------------
  679. // TEDocument::AdjustHV -    Turn off the controls by jamming a zero into vertical
  680. //                            scrollbar. The vertical max is calculated by comparing
  681. //                            the number of lines to the vertical size of the viewRect.
  682. //                            The horizontal max is calculated by comparing the
  683. //                            maximum document width to the width of the viewRect.
  684. //                            The current values are set by comparing the offset
  685. //                            between the view and destination rects. If necessary,
  686. //                            redraw the control by calling ShowControl.
  687. //
  688.     void TEDocument::AdjustHV( Boolean isVert, Boolean mustRedraw )
  689.     {
  690.             short value, lines, max;
  691.             short oldValue, oldMax;
  692.             TEPtr te;
  693.             ControlHandle control;
  694.         
  695.             if (isVert)
  696.                 control = fDocVScroll;
  697.             else
  698.                 control = fDocHScroll;
  699.             oldValue = GetControlValue( control );
  700.             oldMax = GetControlMaximum( control );
  701.             te = *fDocTE;                            // point to TERec for convenience 
  702.             if ( isVert )
  703.             {
  704.                 lines = te->nLines;
  705.                 // since nLines isn’t right if the last character is a return, check for that case
  706.                     if ( *(*te->hText + te->teLength - 1) == kCrChar )
  707.                         lines += 1;
  708.                     max = lines - (( te->viewRect.bottom - te->viewRect.top ) / te->lineHeight);
  709.             }
  710.             else
  711.                 max = kMaxDocWidth - ( te->viewRect.right - te->viewRect.left );
  712.             
  713.             if ( max < 0 )
  714.                 max = 0;
  715.             SetControlMaximum( control, max );
  716.             
  717.             // Must deref. after SetCtlMax since, technically, it could draw and therefore move
  718.             // memory. This is why we don’t just do it once at the beginning.
  719.                 te = *fDocTE;
  720.                 if ( isVert )
  721.                   value = ( te->viewRect.top - te->destRect.top ) / te->lineHeight;
  722.                 else value = te->viewRect.left - te->destRect.left;
  723.                 
  724.                 if ( value < 0 )
  725.                     value = 0;
  726.                 else
  727.                     if ( value >  max )
  728.                         value = max;
  729.                 
  730.                 SetControlValue( control, value );
  731.                 
  732.             // now redraw the control if asked to or if a setting changed 
  733.                 if ( mustRedraw || ( max != oldMax ) || ( value != oldValue ))
  734.                     ShowControl( control );
  735.                 
  736.     } /* TEDocument::AdjustHV */
  737.  
  738.  
  739. //-----------------------------------------------------------------------
  740. // TEDocument::AdjustScrollValues -    Simply call the common adjust routine for
  741. //                                    the vertical and horizontal scrollbars.
  742. //
  743.     void TEDocument::AdjustScrollValues( Boolean mustRedraw )
  744.     {
  745.         AdjustHV( true, mustRedraw );
  746.         AdjustHV( false, mustRedraw );
  747.         
  748.     } /* TEDocument::AdjustScrollValues */
  749.  
  750.  
  751. //-----------------------------------------------------------------------
  752. // TEDocument::GetClickLoop -    
  753. //
  754.     TEClickLoopUPP TEDocument::GetClickLoop( void )
  755.     {
  756.         return fDocClick;
  757.         
  758.     } /* TEDocument::GetClickLoop */
  759.  
  760.  
  761. //-----------------------------------------------------------------------
  762. // TEDocument::GetTEHandle -    
  763. //
  764.     TEHandle TEDocument::GetTEHandle( void )
  765.     {
  766.         return fDocTE;
  767.         
  768.     } /* TEDocument::GetTEHandle */
  769.  
  770.  
  771. //-----------------------------------------------------------------------
  772. // TEDocument::DoCut -    
  773. //
  774.     void TEDocument::DoCut( void )
  775.     {
  776.         long total, contig;
  777.     
  778.         if ( ZeroScrap() == noErr )
  779.         {
  780.             PurgeSpace(&total, &contig);
  781.             if ((*fDocTE)->selEnd - (*fDocTE)->selStart + kTESlop > contig )
  782.                 AlertUser( kTEDocErrStrings,eNoSpaceCut );
  783.             else
  784.             {
  785.                 TECut( fDocTE );
  786.                 if ( TEToScrap() != noErr )
  787.                 {
  788.                     AlertUser( kTEDocErrStrings, eNoCut );
  789.                     (void) ZeroScrap();
  790.                 }
  791.             }
  792.         }
  793.         AdjustScrollbars(false);
  794.         AdjustTE();
  795.         
  796.     } /* TEDocument::DoCut */
  797.  
  798.  
  799. //-----------------------------------------------------------------------
  800. // TEDocument::DoCopy -    
  801. //
  802.     void TEDocument::DoCopy( void )
  803.     {
  804.         if ( ZeroScrap() == noErr )
  805.         {
  806.             TECopy(fDocTE);                        // after copying, export the TE scrap
  807.             if ( TEToScrap() != noErr )
  808.             {
  809.                 AlertUser(kTEDocErrStrings,eNoCopy);
  810.                 ZeroScrap();
  811.             }
  812.         }
  813.         AdjustScrollbars(false);
  814.         AdjustTE();
  815.         
  816.     } /* TEDocument::DoCopy */
  817.  
  818.  
  819. //-----------------------------------------------------------------------
  820. // TEDocument::DoPaste -    
  821. //
  822.     void TEDocument::DoPaste( void )
  823.     {
  824.         Handle aHandle;
  825.         long oldSize, newSize;
  826.         OSErr saveErr;
  827.     
  828.         if ( TEFromScrap() == noErr )
  829.         {
  830.             if ( TEGetScrapLength() + ((*fDocTE)->teLength - ((*fDocTE)->selEnd - (*fDocTE)->selStart )) > kMaxTELength )
  831.                 AlertUser( kTEDocErrStrings, eExceedPaste );
  832.             else
  833.             {
  834.                 aHandle = (Handle) TEGetText( fDocTE );
  835.                 oldSize = GetHandleSize( aHandle );
  836.                 newSize = oldSize + TEGetScrapLength() + kTESlop;
  837.                 SetHandleSize( aHandle, newSize );
  838.                 saveErr = MemError();
  839.                 SetHandleSize( aHandle, oldSize );
  840.                 
  841.                 if (saveErr != noErr)
  842.                     AlertUser( kTEDocErrStrings, eNoSpacePaste );
  843.                 else
  844.                     TEPaste(fDocTE);
  845.             }
  846.         }
  847.         else
  848.             AlertUser( kTEDocErrStrings, eNoPaste );
  849.             
  850.         AdjustScrollbars( false );
  851.         AdjustTE();
  852.         
  853.     } /* TEDocument::DoPaste */
  854.  
  855.  
  856. //-----------------------------------------------------------------------
  857. // TEDocument::DoClear -    
  858. //
  859.     void TEDocument::DoClear( void )
  860.     {
  861.         TEDelete( fDocTE );
  862.         AdjustScrollbars( false );
  863.         AdjustTE();
  864.         
  865.     } /* TEDocument::DoClear */
  866.  
  867.  
  868.  
  869. /***********************************************************************/
  870. //
  871. // Global declarations
  872. //            Routines used by this class, which don't belong to the class since we use
  873. //            them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
  874. //
  875. /***********************************************************************/
  876.  
  877.  
  878. //-----------------------------------------------------------------------
  879. // CommonAction -    Common algorithm for pinning the value of a control.
  880. //                    It returns the actual amount the value of the control
  881. //                    changed.
  882. //
  883.     void CommonAction( ControlHandle control, short* amount )
  884.     {
  885.         short        value, max;
  886.         
  887.         value = GetControlValue( control );
  888.         max = GetControlMaximum( control );
  889.         *amount = value - *amount;
  890.         
  891.         if ( *amount <= 0 )
  892.             *amount = 0;
  893.         else
  894.             if ( *amount >= max )
  895.                 *amount = max;
  896.                 
  897.         SetControlValue(control, *amount);
  898.         *amount = value - *amount;
  899.         
  900.     } /* CommonAction */
  901.  
  902.  
  903. //-----------------------------------------------------------------------
  904. // VActionProc -    Determines how much to change the value of the vertical
  905. //                     scrollbar by and how much to scroll the TE record.
  906. //
  907.     pascal void VActionProc( ControlHandle control, short part )
  908.     {
  909.         short        amount;
  910.         WindowPtr    window;
  911.         TEPtr        te;
  912.         TEDocument* doc;
  913.     
  914.         if ( part != 0 )
  915.         {
  916.             window = (*control)->contrlOwner;
  917.             doc = GetWindowDoc( window );
  918.             te = *(doc->GetTEHandle() );
  919.             switch ( part )
  920.             {
  921.                 case kControlUpButtonPart:                            // one line
  922.                 case kControlDownButtonPart:        
  923.                                         amount = 1;                         
  924.                                         break;
  925.                                         
  926.                 case kControlPageUpPart:                                // one page 
  927.                 case kControlPageDownPart:
  928.                                         amount = (te->viewRect.bottom - te->viewRect.top) / te->lineHeight;
  929.                                         break;
  930.             }
  931.               
  932.             if (( part == kControlDownButtonPart ) || ( part == kControlPageDownPart ))
  933.                 amount = -amount;                            // reverse direction for a downer 
  934.             CommonAction( control, &amount );
  935.             
  936.             if ( amount != 0 )
  937.                 TEScroll( 0, amount * te->lineHeight, doc->GetTEHandle());
  938.         }
  939.     } /* VActionProc */ 
  940.  
  941.  
  942. //-----------------------------------------------------------------------
  943. // HActionProc -    Determines how much to change the value of the horizontal
  944. //                     scrollbar by and how much to scroll the TE record.
  945. //
  946.     pascal void HActionProc( ControlHandle control, short part )
  947.     {
  948.         short        amount;
  949.         WindowPtr    window;
  950.         TEPtr        te;
  951.         TEDocument* doc;
  952.     
  953.         if ( part != 0 )
  954.         {
  955.             window = (*control)->contrlOwner;
  956.             doc = GetWindowDoc( window );
  957.             te = *(doc->GetTEHandle());
  958.             switch ( part )
  959.             {
  960.                 case kControlUpButtonPart:                            // a few pixels
  961.                 case kControlDownButtonPart:        
  962.                                         amount = 4;
  963.                                         break;
  964.                                         
  965.                 case kControlPageUpPart:                                // a page 
  966.                 case kControlPageDownPart:        
  967.                                         amount = te->viewRect.right - te->viewRect.left;
  968.                                         break;
  969.             }
  970.             
  971.             if (( part == kControlDownButtonPart ) || ( part == kControlPageDownPart ))
  972.                 amount = -amount;                            // reverse direction 
  973.             CommonAction( control, &amount );
  974.             
  975.             if ( amount != 0 )
  976.                 TEScroll( amount, 0, doc->GetTEHandle());
  977.         }
  978.     } /* HActionProc */
  979.  
  980.  
  981. //-----------------------------------------------------------------------
  982. // PascalClickLoop -Gets called from our assembly language routine (on the
  983. //                  68K, C on the PowerPC), ASMCLICKLOOP,
  984. //                    which is in turn called by the TEClick toolbox routine.
  985. //                    Saves the windows clip region, sets it to the portRect,
  986. //                    adjusts the scrollbar values to match the TE scroll amount,
  987. //                    then restores the clip region.
  988. //
  989.     pascal void PascalClickLoop(void)
  990.     {
  991.         RgnHandle    region;
  992.         WindowPtr wind;
  993.         TEDocument* doc;
  994.     
  995.         wind = FrontWindow();
  996.         doc = GetWindowDoc( wind );
  997.         region = NewRgn();
  998.         GetClip( region );                        // save clip 
  999.         ClipRect( &wind->portRect );
  1000.         doc->AdjustScrollValues( false );
  1001.         SetClip( region );                        // restore clip 
  1002.         DisposeRgn( region );
  1003.         
  1004.     } /* PascalClickLoop */
  1005.  
  1006.  
  1007. //-----------------------------------------------------------------------
  1008. // GetOldClickLoop -Gets called from our assembly language routine (on the
  1009. //                  68K, C on the PowerPC), ASMCLICKLOOP,
  1010. //                    which is in turn called by the TEClick toolbox routine. It
  1011. //                    returns the address of the default ClickLoop routine that was
  1012. //                    put into the TERec by TEAutoView to ASMCLICKLOOP so that it
  1013. //                    can call it.
  1014. //
  1015.    pascal TEClickLoopUPP GetOldClickLoop(void)
  1016.     {
  1017.         TEDocument* doc;
  1018.     
  1019.         doc = GetWindowDoc( FrontWindow());
  1020.         if (doc == nil)
  1021.             return nil;
  1022.             
  1023.         return doc->GetClickLoop();
  1024.         
  1025.     } /* GetOldClickLoop */
  1026.  
  1027.  
  1028. //-----------------------------------------------------------------------
  1029. // AlertUser -    
  1030. //
  1031.     void AlertUser( short errResID, short errCode )
  1032.     {
  1033.         Str255 message;
  1034.     
  1035.         SetCursor( &qd.arrow );
  1036.         GetIndString( message, errResID, errCode );
  1037.         ParamText( message,(ConstStr255Param) "\p", (ConstStr255Param)"\p", (ConstStr255Param)"\p" );
  1038.         (void) Alert( rUserAlert,  nil );
  1039.         
  1040.     } /* AlertUser */
  1041.  
  1042. #if GENERATINGCFM
  1043. //------------------------------------------------------------------------
  1044. // ASMCLICKLOOP - On the 68K we have to have an assembler routine do this
  1045. //               because registers must be mucked with.  On the PowerPC
  1046. //               we have MixedMode which gives us a C interface to a
  1047. //               TEClickLoopProc ();
  1048. //
  1049. //               Our job is to 1) Call the old, default click loop routine
  1050. //               that scrolls text. 2) Call our own routine that handles
  1051. //               tracking the scroll bars to follow along.
  1052. //
  1053.      pascal Boolean ASMCLICKLOOP (TEPtr pTE)
  1054.       {
  1055.          TEClickLoopUPP Old_ClickLoopRoutine;
  1056.             
  1057.          Old_ClickLoopRoutine = GetOldClickLoop ();
  1058.          CallTEClickLoopProc (Old_ClickLoopRoutine, pTE);
  1059.          
  1060.          CallUniversalProc((UniversalProcPtr)(Old_ClickLoopRoutine), uppTEClickLoopProcInfo, pTE);
  1061.          
  1062.          PascalClickLoop ();
  1063.          
  1064.          return(true);
  1065.       
  1066.       } /* ASMCLICKLOOP */
  1067.  
  1068. #endif